package com.system.zuul.fliter;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.system.zuul.model.ApiKey;
import com.system.zuul.service.ApiKeyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

public class ApiFliter extends ZuulFilter {

    private final String APISERVICE_URL = "/api-service";

    private final ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();

    @Autowired
    ApiKeyService apiKeyService;

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String url = request.getRequestURL().toString();
        if (url.contains(APISERVICE_URL)) {
            return true;
        }
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String apiKey = request.getParameter("key");
        if (null == apiKey || apiKeyService.getApiKeyCount(apiKey)==0) {
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            requestContext.setResponseBody("Your key is illegal");
        }else {
            ApiKey apiKeyInfo = apiKeyService.getApiKey(apiKey);
            if (apiKeyInfo.getAllRequestCount() >= apiKeyInfo.getMonthlyRequestLimit()) {
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
                requestContext.setResponseBody("The api requests limit of this month has been reached");
            } else {
                apiKeyService.addRequestCount(apiKey);
            }
            RateLimiter limiter = null != rateLimiterMap.get(apiKey) ? rateLimiterMap.get(apiKey) : RateLimiter.create(apiKeyInfo.getPreSecondRequestLimit());
            // 1秒内被阻塞，直接返回429
            if (!limiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
                requestContext.setResponseBody("Too many requests");
            }
        }
        return null;
    }
}
